package workflow;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import workflow.core.WorkflowBlock;
import workflow.core.WorkflowElement;
import workflow.core.WorkflowRunableElement;
import workflow.dto.WorkflowConnection;
import workflow.dto.WorkflowProcessDefinition;

public final class WorkflowUtil {

	private WorkflowUtil() {
	}

	/**
	 * parsuje xml z definicja procesu tworzac nowy obiekt
	 * WorkflowProcessDefinition
	 */
	public static WorkflowProcessDefinition parseXMLProcessDefinition(
			File xmlFile) {

		WorkflowProcessDefinition def = new WorkflowProcessDefinition();
		try {

			SAXReader reader = new SAXReader();
			Document doc = reader.read(xmlFile);
			Element root = doc.getRootElement();

			for (Iterator i = root.elementIterator(); i.hasNext();) {
				Element elem = (Element) i.next();
				if (elem.getName().equals("connections")) {
					def.setConnections(getConnectionsFromXMLNode(elem));
				} else if (elem.getName().equals("elementsParameters")) {
					def.setElementsParameters(getElementsParametersFromXMLNode(elem));
				} else if (elem.getName().equals("blocks")) {
					def.setWorkflowBlocks(getWorkflowBlocksFromXMLNode(elem));
				}
			}
			
			//def.initizalizeElementsParameters();

		} catch (Exception e) {
			e.printStackTrace();
			// TODO: zaloguj to
		}
		return def;
	}

	/**
	 * tworzy liste z WorkflowBlockami na podstawie wezla xml zawierajacego te
	 * dane
	 */
	private static List<WorkflowBlock> getWorkflowBlocksFromXMLNode(Element elem) {

		List<WorkflowBlock> blocksList = new ArrayList<WorkflowBlock>();

		// wybieram pojedynczy block
		for (Iterator i = elem.elementIterator(); i.hasNext();) {
			try {
				Element block = (Element) i.next();
				Class blockClass = null;
				String blockName = null;
				List<WorkflowElement> elements = new ArrayList<WorkflowElement>();

				// pobieram dane dotyczace bloku: nazwa, klasa oraz jego elementy
				for (Iterator j = block.elementIterator(); j.hasNext();) {
					Element blockElem = (Element) j.next();

					if (blockElem.getName().equals("name")) {
						blockName = blockElem.getText();
					} else if (blockElem.getName().equals("class")) {
						blockClass = Class.forName(blockElem.getText());
					} else if (blockElem.getName().equals("elements")) {
						elements = getWorkflowElementsFromXmlNode(blockElem);
					}
				}
				// tworze WorkflowBlock i go dodaje do listy blokow
				WorkflowBlock bl = (WorkflowBlock) blockClass.newInstance();
				bl.setElements(elements);
				bl.setName(blockName);
				blocksList.add(bl);
			} catch (Exception ex) {
				// jak cos zle pojdzie to nie doda tego elementu
				// TODO: zaloguj to
			}
		}
		return blocksList;
	}

	/** tworzy liste WorkflowElementow na podstawie przekazanego wezla xml */
	private static List<WorkflowElement> getWorkflowElementsFromXmlNode(
			Element blockNode) {
		
		List<WorkflowElement> elementsList = new ArrayList<WorkflowElement>();
		
		for (Iterator i = blockNode.elementIterator(); i.hasNext();) {
			try {
				Element element = (Element) i.next();
				Class elementClass = null;
				String elementName = null;

				// pobieram dane dotyczace elementu: nazwa, klasa
				for (Iterator j = element.elementIterator(); j.hasNext();) {
					Element elemData = (Element) j.next();

					if (elemData.getName().equals("name")) {
						elementName = elemData.getText();
					} else if (elemData.getName().equals("class")) {
						elementClass = Class.forName(elemData.getText());
					}
				}
				int z = 10;
				// tworze WorkflowElement i go dodaje do listy elementow
				WorkflowElement el = (WorkflowElement) elementClass.getConstructor(String.class).newInstance("");
				el.setName(elementName);
				elementsList.add(el);
			} catch (Exception ex) {
				// jak cos zle pojdzie to nie doda tego elementu
				// TODO: zaloguj to
			}
		}
		return elementsList;
	}

	/** tworzy mape z parametrami na podstawie wezla xml zawierajacego te dane */
	private static Map<String, Map<String, String>> getElementsParametersFromXMLNode(
			Element paramsNode) {

		Map<String, Map<String, String>> elementsParameters = new HashMap<String, Map<String,String>>();
		
		// pobieram elementParameters
		for (Iterator i = paramsNode.elementIterator(); i.hasNext();) {
			Element elementData = (Element) i.next();
			
			String elementKey = null;
			Map<String, String> elementMap = new HashMap<String,String>();
			
			// pobieram jego dane - elementKey oraz elementMap
			for (Iterator j = elementData.elementIterator(); j.hasNext();) {
				Element data = (Element) j.next();
				
				if(data.getName().equals("elementKey")){
					elementKey = data.getText();
				}else if(data.getName().equals("parameters")){
					// wyciagam mape z parametrami
					for (Iterator k = data.elementIterator(); k.hasNext();) {
						Element parameters = (Element) k.next();
						String key = null;
						String value = null;
						
						// wyciagam pojedynczy parametr
						for (Iterator l = parameters.elementIterator(); l.hasNext();) {
							Element parameter = (Element) l.next();
							if(parameter.getName().equals("key")){
								key = parameter.getText();
							}else if(parameter.getName().equals("value")){
								value = parameter.getText();
							}
						}
						// dodaje parametr do mapy
						elementMap.put(key, value);
					}
				}
			}
			// dodaje dane dotyczace tego elementu do mapy elementsParameters
			elementsParameters.put(elementKey, elementMap);
		}
		
		return elementsParameters;
	}

	/** tworzy liste polaczen na podstawie wezla xml zawierajacego te dane */
	private static List<WorkflowConnection> getConnectionsFromXMLNode(
			Element elem) {

		List<WorkflowConnection> connList = new ArrayList<WorkflowConnection>();

		// wybieram pojedynczy connection
		for (Iterator i = elem.elementIterator(); i.hasNext();) {
			String source = null;
			Map<Integer, String> targets = new HashMap<Integer, String>();

			Element connection = (Element) i.next();
			// pobieram source oraz targety
			for (Iterator j = connection.elementIterator(); j.hasNext();) {
				Element connElem = (Element) j.next();
				int targetId = 0;
				if (connElem.getName().equals("targets")) {
					// pobieram targety -> WAZNA JEST KOLEJNOSC ELEMENTOW TARGET
					for (Iterator k = connElem.elementIterator(); k.hasNext();) {
						Element targetElem = (Element) k.next();
						targets.put(targetId, targetElem.getText());
						targetId++;
					}
				} else if (connElem.getName().equals("source")) {
					// pobieram source
					source = connElem.getText();
				}
			}
			WorkflowConnection conn = new WorkflowConnection(source, targets);
			connList.add(conn);
		}
		return connList;
	}


}
